package sys

import (
	"iot/common"
	"sync"
)

var (
	eventBusInstance *EventBus
	eventBusOnce     sync.Once
)

type EventBus struct {
	mu       sync.RWMutex
	handlers map[common.Topic][]func(args ...interface{})
}

func Bus() *EventBus {
	eventBusOnce.Do(func() {
		eventBusInstance = &EventBus{
			handlers: make(map[common.Topic][]func(args ...interface{})),
		}
	})
	return eventBusInstance
}

func (b *EventBus) Subscribe(topic common.Topic, handler func(args ...interface{})) {
	b.mu.Lock()
	defer b.mu.Unlock()
	b.handlers[topic] = append(b.handlers[topic], handler)
}

func (b *EventBus) Unsubscribe(topic common.Topic, handler func(args ...interface{})) {
	b.mu.Lock()
	defer b.mu.Unlock()

	handlers, ok := b.handlers[topic]
	if !ok {
		return
	}

	for i, h := range handlers {
		if &h == &handler {
			b.handlers[topic] = append(handlers[:i], handlers[i+1:]...)
			return
		}
	}
}

func (b *EventBus) Publish(topic common.Topic, args ...interface{}) {
	b.mu.RLock()
	handlers := b.handlers[topic]
	b.mu.RUnlock()

	for _, handler := range handlers {
		handler(args...)
	}
}

func (b *EventBus) PublishAsync(topic common.Topic, args ...interface{}) {
	b.mu.RLock()
	handlers := b.handlers[topic]
	b.mu.RUnlock()

	for i := range handlers {
		go func(handler func(args ...interface{})) {
			handler(args...)
		}(handlers[i])
	}
}
